/*********************************************************************
* File: engineApi.cpp
*
* \brief Implementation of Half-Life engine functions.
*
* \author Instituto Superior Tecnico de Lisboa, Portugal
* \author Technical University of Lisbon, Portugal
* 
* \author Agentes Autonomos e Sistemas Multiagente
* \author Autonomous Agents and Multiagent Systems
* 
* \version	1.0
* \date		2006/2007
*********************************************************************/
#include "engineApi.h"
#include "metaApi.h"
#include "game/gui.h"
#include "game/sounds.h"
#include "util/util.h"
#include "util/debug.h"
using namespace util;
#include "agents/globals.h"
#include "agents/Agent.h"
using namespace agents;

void (*agentMsgFunction)(void *, int) = NULL;

int g_msgIndex;
int g_msgState;

// messages created in RegUserMsg which will be "caught"
int message_VGUIMenu = 0;
int message_ShowMenu = 0;
int message_WeaponList = 0;
int message_CurWeapon = 0;
int message_AmmoX = 0;
int message_AmmoPickup = 0;
int message_Health = 0;
int message_Battery = 0;
int message_Damage = 0;
int message_Money = 0;
int message_StatusIcon = 0;	// for buyzone, defuser, bombzone
int message_DeathMsg = 0;
int message_ScreenFade = 0;
int message_HLTV = 0;
int message_TextMsg = 0;

int message_SayText = 0;

void pfnChangeLevel(char* s1, char* s2)
{
   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   CHANGE_LEVEL(s1, s2);
}

/**
*
**/
edict_t* pfnFindEntityByString(edict_t *pEdictStartSearchAfter, const char *pszField, const char *pszValue)
{
   if (FStrEq(pszValue, "info_map_parameters"))
	   g_world->newRound();

   if (g_bIsMMPlugin)
      RETURN_META_VALUE(MRES_IGNORED, 0);

   return FIND_ENTITY_BY_STRING(pEdictStartSearchAfter, pszField, pszValue);
}


/**
*
**/
void pfnEmitSound(edict_t *entity, int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch)
{
   soundAttachToThreat(entity, sample, volume);

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   EMIT_SOUND_DYN2(entity, channel, sample, volume, attenuation, fFlags, pitch);
}

/**
*
**/
void pfnClientCommand(edict_t *pEdict, char *szFmt, ...)
{
   if (!util::isPlayer(pEdict))
   {
      if (g_bIsMMPlugin)
         RETURN_META(MRES_SUPERCEDE);
      return;
   }

   char string[256];
   va_list argptr;
   va_start(argptr, szFmt);
   _vsnprintf(string, sizeof(string), szFmt, argptr);
   va_end(argptr);

   if ((pEdict->v.flags & FL_FAKECLIENT) || Agent::getInstance(pEdict))
   {
      util::fakeClientCommand(pEdict, string);
      if (g_bIsMMPlugin)
         RETURN_META(MRES_SUPERCEDE);
   }
   else
   {
      if (g_bIsMMPlugin)
         RETURN_META(MRES_IGNORED);
      CLIENT_COMMAND(pEdict, string);
   }
}

/**
* \brief Should be called to start a network message.
**/
void pfnMessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed)
{
   g_msgState = 0; // reset the message state machine

   if (g_bIsMMPlugin && message_Money == 0)
   {
      // Store the message type in our own variables, since the
      // GET_USER_MSG_ID() will just do a lot of strcmp()'s...
      message_ShowMenu = GET_USER_MSG_ID(PLID, "ShowMenu", NULL);
      message_VGUIMenu = GET_USER_MSG_ID(PLID, "VGUIMenu", NULL);
      message_WeaponList = GET_USER_MSG_ID(PLID, "WeaponList", NULL);
      message_CurWeapon = GET_USER_MSG_ID(PLID, "CurWeapon", NULL);
      message_AmmoX = GET_USER_MSG_ID(PLID, "AmmoX", NULL);
      message_AmmoPickup = GET_USER_MSG_ID(PLID, "AmmoPickup", NULL);
      message_Damage = GET_USER_MSG_ID(PLID, "Damage", NULL);
      message_Money = GET_USER_MSG_ID(PLID, "Money", NULL);
      message_StatusIcon = GET_USER_MSG_ID(PLID, "StatusIcon", NULL);
      message_DeathMsg = GET_USER_MSG_ID(PLID, "DeathMsg", NULL);
      message_ScreenFade = GET_USER_MSG_ID(PLID, "ScreenFade", NULL);
      message_HLTV = GET_USER_MSG_ID(PLID, "HLTV", NULL);
      message_TextMsg = GET_USER_MSG_ID(PLID, "TextMsg", NULL);

	  message_SayText = GET_USER_MSG_ID(PLID, "SayText", NULL);
   }

   // If the money message isn't registered, then we aren't running CS
   // Since POD-Agent is for CS only, just print an error message and bomb out here
   if (message_Money == 0)
      util::terminateOnError("POD-Agent can only be used in Counter-Strike!\n");

   if (gpGlobals->deathmatch)
   {
      int index;

      if (msg_dest == MSG_SPEC && msg_type == message_HLTV)
         agentMsgFunction = game::gui::client_CS_HLTV;

      if (msg_type == message_WeaponList)
         agentMsgFunction = game::gui::client_CS_WeaponList;

      if (ed)
      {
         index = util::getAgentIndex(ed);

         // is this message for an agent?
         if (index != -1)
         {
            agentMsgFunction = NULL;  // no msg function until known otherwise
            g_msgIndex = index;    // index of agent receiving message

			// Message handling is done in bot_client.cpp
            if (msg_type == message_VGUIMenu)
               agentMsgFunction = game::gui::client_CS_VGUI;
            else if (msg_type == message_ShowMenu)
               agentMsgFunction = game::gui::client_CS_ShowMenu;
            else if (msg_type == message_CurWeapon)
               agentMsgFunction = game::gui::client_CS_CurrentWeapon;
            else if (msg_type == message_AmmoX)
               agentMsgFunction = game::gui::client_CS_AmmoX;
            else if (msg_type == message_AmmoPickup)
               agentMsgFunction = game::gui::client_CS_AmmoPickup;
            else if (msg_type == message_Damage)
               agentMsgFunction = game::gui::client_CS_Damage;
            else if (msg_type == message_Money)
               agentMsgFunction = game::gui::client_CS_Money;
            else if (msg_type == message_StatusIcon)
               agentMsgFunction = game::gui::client_CS_StatusIcon;
            else if (msg_type == message_ScreenFade)
               agentMsgFunction = game::gui::client_CS_ScreenFade;
            else if (msg_type == message_TextMsg)
               agentMsgFunction = game::gui::client_CS_TextMsg;
			else if(msg_type == message_SayText)
				agentMsgFunction = game::gui::client_CS_SayText;
         }
      }
      else if (msg_dest == MSG_ALL)
      {
         agentMsgFunction = NULL;  // no msg function until known otherwise
         g_msgIndex = -1;       // index of agent receiving message (none)

         if (msg_type == message_DeathMsg)
            agentMsgFunction = game::gui::client_CS_DeathMsg;
         if (msg_type == message_TextMsg)
            agentMsgFunction = game::gui::client_CS_TextMsgAll;
         else if (msg_type == SVC_INTERMISSION)
         {
            //...
         }
      }
   }

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   MESSAGE_BEGIN(msg_dest, msg_type, pOrigin, ed);
}

/**
* \brief Should be called to end a network message.
**/
void pfnMessageEnd(void)
{
   g_msgState = 0;
   agentMsgFunction = NULL;

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   MESSAGE_END();
}

/**
* \brief Write a byte to the message.
*
* \param[in] iValue The byte.
**/
void pfnWriteByte(int iValue)
{
   // if this message is for an agent, call the client message function...
   if (agentMsgFunction)
      (*agentMsgFunction)((void *)&iValue, g_msgIndex);

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   WRITE_BYTE(iValue);
}

/**
* \brief Write a char to the message.
*
* \param[in] iValue The char.
**/
void pfnWriteChar(int iValue)
{
   // if this message is for an agent, call the client message function...
   if (agentMsgFunction)
      (*agentMsgFunction)((void *)&iValue, g_msgIndex);

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   WRITE_CHAR(iValue);
}

/**
* \brief Write a short to the message.
*
* \param[in] iValue The short.
**/
void pfnWriteShort(int iValue)
{
   // if this message is for an agent, call the client message function...
   if (agentMsgFunction)
      (*agentMsgFunction)((void *)&iValue, g_msgIndex);

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   WRITE_SHORT(iValue);
}

/**
* \brief Write a long to the message.
*
* \param[in] iValue The long.
**/
void pfnWriteLong(int iValue)
{
   // if this message is for an agent, call the client message function...
   if (agentMsgFunction)
      (*agentMsgFunction)((void *)&iValue, g_msgIndex);

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   WRITE_LONG(iValue);
}

/**
* \brief Write an angle to the message.
*
* \param[in] flValue The angle.
**/
void pfnWriteAngle(float flValue)
{
   // if this message is for an agent, call the client message function...
   if (agentMsgFunction)
      (*agentMsgFunction)((void *)&flValue, g_msgIndex);

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   WRITE_ANGLE(flValue);
}

/**
* \brief Write a coordinate to the message.
*
* \param[in] flValue The coordinate.
**/
void pfnWriteCoord(float flValue)
{
   // if this message is for an agent, call the client message function...
   if (agentMsgFunction)
      (*agentMsgFunction)((void *)&flValue, g_msgIndex);

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   WRITE_COORD(flValue);
}

/**
* \brief Write a string to the message.
*
* \param[in] sz The string.
**/
void pfnWriteString(const char *sz)
{
   if (gpGlobals->deathmatch)
   {
      // Check if it's the "Bomb Planted" Message
      if (FStrEq(sz, "%!MRAD_BOMBPL"))
      {
         if (!g_world->isBombPlanted())
         {
			 g_world->setIsBombPlanted(true);
            g_world->setTimeBombWasPlanted(gpGlobals->time);
         }
      }
   }

   // if this message is for an agent, call the client message function...
   if (agentMsgFunction)
      (*agentMsgFunction)((void *)sz, g_msgIndex);

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   WRITE_STRING(sz);
}

/**
* \brief Write an entity to the message.
*
* \param[in] iValue The entity.
**/
void pfnWriteEntity(int iValue)
{
   // if this message is for an agent, call the client message function...
   if (agentMsgFunction)
      (*agentMsgFunction)((void *)&iValue, g_msgIndex);

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   WRITE_ENTITY(iValue);
}

// Pierre-Marie Baty - START (http://racc.bots-united.com)
/**
*
**/
int pfnCmd_Argc(void)
{
   // is this an agent issuing that client command ?
   if (g_isFakeClientCommand)
   {
      if (g_bIsMMPlugin)
         RETURN_META_VALUE(MRES_SUPERCEDE, g_fakeArgCount);
      return g_fakeArgCount; // if so, then return the argument count we know
   }

   if (g_bIsMMPlugin)
      RETURN_META_VALUE(MRES_IGNORED, NULL);

   return CMD_ARGC(); // ask the engine how many arguments there are
}

/**
*
**/
const char *pfnCmd_Args(void)
{
   // is this an agent issuing that client command?
   if (g_isFakeClientCommand)
   {
      // is it a "say" or "say_team" client command?
      if (strncmp ("say ", g_argv, 4) == 0)
      {
         if (g_bIsMMPlugin)
            RETURN_META_VALUE(MRES_SUPERCEDE, g_argv + 4);
         return g_argv + 4; // skip the "say" agent client command
      }
      else if (strncmp ("say_team ", g_argv, 9) == 0)
      {
         if (g_bIsMMPlugin)
            RETURN_META_VALUE(MRES_SUPERCEDE, g_argv + 9);
         return g_argv + 9; // skip the "say_team" agent client command
      }

      if (g_bIsMMPlugin)
         RETURN_META_VALUE(MRES_SUPERCEDE, g_argv);
      return g_argv; // else return the whole agent client command string we know
   }

   if (g_bIsMMPlugin)
      RETURN_META_VALUE(MRES_IGNORED, NULL);

   return CMD_ARGS(); // ask the client command string to the engine
}

/**
*
**/
const char *pfnCmd_Argv(int argc)
{
   // is this an agent issuing that client command ?
   if (g_isFakeClientCommand)
   {
      if (g_bIsMMPlugin)
         RETURN_META_VALUE(MRES_SUPERCEDE, util::getArg(g_argv, argc));
      return util::getArg(g_argv, argc); // if so, then return the wanted argument we know
   }
   if (g_bIsMMPlugin)
      RETURN_META_VALUE(MRES_IGNORED, NULL);

   return CMD_ARGV(argc); // ask the argument number "argc" to the engine
}
// Pierre-Marie Baty - END

/**
* \brief Agent's implementation of the 'pfnClientPrintf' function. 
*
* \see HL-SDK documentation.
**/
void pfnClientPrintf( edict_t* pEdict, PRINT_TYPE ptype, const char *szMsg )
{
   if (!util::isPlayer(pEdict) || (pEdict->v.flags & FL_FAKECLIENT) ||
      Agent::getInstance(pEdict))
   {
      if (g_bIsMMPlugin)
         RETURN_META(MRES_SUPERCEDE);
      return;
   }

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   CLIENT_PRINTF(pEdict, ptype, szMsg);
}

/**
* \brief Agent's implementation of the 'pfnSetClientMaxspeed' function. 
*
* \see HL-SDK documentation.
**/
void pfnSetClientMaxspeed(const edict_t *pEdict, float fNewMaxspeed)
{
   ((edict_t *)pEdict)->v.maxspeed = fNewMaxspeed;

   if (g_bIsMMPlugin)
      RETURN_META(MRES_IGNORED);

   (*g_engfuncs.pfnSetClientMaxspeed)(pEdict, fNewMaxspeed);
}

/**
* \brief Agent's implementation of the 'pfnRegUserMsg' function. 
*
* \see HL-SDK documentation.
**/
int pfnRegUserMsg(const char *pszName, int iSize)
{
   if (g_bIsMMPlugin)
      RETURN_META_VALUE(MRES_IGNORED, 0);

   int msg = REG_USER_MSG(pszName, iSize);

   if (strcmp(pszName, "ShowMenu") == 0)
      message_ShowMenu = msg;
   else if (strcmp(pszName, "VGUIMenu") == 0)
      message_VGUIMenu = msg;
   else if (strcmp(pszName, "WeaponList") == 0)
      message_WeaponList = msg;
   else if (strcmp(pszName, "CurWeapon") == 0)
      message_CurWeapon = msg;
   else if (strcmp(pszName, "AmmoX") == 0)
      message_AmmoX = msg;
   else if (strcmp(pszName, "AmmoPickup") == 0)
      message_AmmoPickup = msg;
   else if (strcmp(pszName, "Damage") == 0)
      message_Damage = msg;
   else if (strcmp(pszName, "Money") == 0)
      message_Money = msg;
   else if (strcmp(pszName, "StatusIcon") == 0)
      message_StatusIcon = msg;
   else if (strcmp(pszName, "DeathMsg") == 0)
      message_DeathMsg = msg;
   else if (strcmp(pszName, "ScreenFade") == 0)
      message_ScreenFade = msg;
   else if (strcmp(pszName, "HLTV") == 0)
      message_HLTV = msg;
   else if (strcmp(pszName, "TextMsg") == 0)
      message_TextMsg = msg;

   else if (strcmp(pszName, "SayText") == 0)
	   message_SayText = msg;

   return msg;
}
